#include "io_sgf.h"
#include "record.h"
#include "step.h"
#include "settings.h"

#include <QTextStream>
#include <QFile>
#include <QString>
#include <QChar>
#include <cassert>
#include <string>

IO_sgf::IO_sgf()
{
}

IO_sgf::~IO_sgf()
{

}

/** 需要修改为通过gameSettings读取相关输出数据 **/
void IO_sgf::outputSGF(QFile *file, Settings *gameSettings, Go *pcore)
{
    QTextStream out(file);

    //输出第一行为标准形式
    out << o_start_tree() << o_start_node() << "GM[1]FF[4]SZ[19]AP[NT_GO:2.0]";
    if(pcore->handicapsValue != 0)
    {
        out << "HA[" << pcore->handicapsValue << "]";
        //输出让子的AB[]属性
        series AB_Series = gameSettings->get_AB();
        vector<coordinate>::iterator iter;
        for(iter=AB_Series.begin(); iter!=AB_Series.end(); iter++)
        {
            out << o_set_stone(BLACK, iter->first, iter->second);
        }
    }
    out << endl;

    //record中读取每一步棋以sgf格式写入文件
    Step *tempStep;
    tempStep = pcore->record->getLastStep();
    if(tempStep!=0)
    {
        unsigned int max = tempStep->getIndex() + 1; //获取循环停止条件
        for(unsigned int i = 1; i<max; i++)
        {
            tempStep = pcore->record->getStep(i);

            out << o_start_node() << o_set_one_move(tempStep->getColor(), tempStep->getCoordinateX(),
                                                    tempStep->getCoordinateY());
        }
    }

    //输出最后
    out << o_end_tree() << endl;
}

bool IO_sgf::inputSGF(QFile *file, Settings *gameSettings, io_series *coos)
{
    tempSettings = gameSettings;
    tempSeries = coos;
    tempFile = file;

    QTextStream in(file);

    if(sgfParseCollection(in))
    {
        tempSettings = 0;
        tempSeries = 0;
        tempFile = 0;
        return true;
    }
    else
    {
        tempSettings = 0;
        tempSeries = 0;
        tempFile = 0;
        return false;
    }
}

//只解析主树, 分支全部忽略
bool IO_sgf::sgfParseCollection(QTextStream &in)
{
    char tempChar;

    while(!tempFile->atEnd()) //搜索到第一个'('为止
    {
        in >> tempChar;

        if(tempChar == '(') return sgfParseTree(in);
    }

    return false;
}

bool IO_sgf::sgfParseTree(QTextStream &in)
{
    char tempChar;

    in >> tempChar;

    if(tempChar == ';') return sgfParseNode(in);
    else return false;
}

bool IO_sgf::sgfParseNode(QTextStream &in)
{
    return sgfParseProperty(in);
}

bool IO_sgf::sgfParseProperty(QTextStream &in) //保证buffer::in在属性ID开头处
{
    QString property;
    char tempChar;

    int root = 0; //记录分支数

    while(1)
    {
        in >> tempChar;

        if(tempChar == '(') //不支持分支, 忽略除了主树以外的所有分支
        {
            root++;
            while(!tempFile->atEnd())
            {
                in >> tempChar;
                if(tempChar == '(') root++;
                else if(tempChar == ')')
                {
                    if(root==0) break;
                    else root--;
                }
            }
        }
        else if(tempChar == ')')
        {
            if(root==0) return true; //各种递归和循环唯一的出口
            else root--;
        }
        else if(tempChar == ';') return sgfParseNode(in);
        else if(tempChar != '[')
        {
            if(tempChar!=' ' || tempChar!='\n') //如果这里是其他乱七八糟的格式则会实际读取无效
            property+=tempChar;
        }
        else break;
    }

    if(property=="GM")
    {
        if(do_GM(in))
        {
            return sgfParseProperty(in);
        }
        else return false;
    }

    else if(property=="FF")
    {
        do_FF(in);
        return sgfParseProperty(in);
    }

    else if(property=="RU")
    {
        do_RU(in);
        return sgfParseProperty(in);
    }

    else if(property=="HA")
    {
        if(do_HA(in))
        {
            return sgfParseProperty(in);
        }
        else return false;
    }

    else if(property=="SZ")
    {
        if(do_SZ(in))
        {
            return sgfParseProperty(in);
        }
        else return false;
    }

    else if(property=="KM")
    {
        if(do_KM(in))
        {
            return sgfParseProperty(in);
        }
        else return false;
    }

    else if(property=="AB")
    {
        if(do_AB(in))
        {
            return sgfParseProperty(in);
        }
        else return false;
    }

    else if(property=="B")
    {
        if(do_BW(in, BLACK))
        {
            return sgfParseProperty(in);
        }
        else return false;
    }

    else if(property=="W")
    {
        if(do_BW(in, WHITE))
        {
            return sgfParseProperty(in);
        }
        else return false;
    }

    else
    {
        do_Default(in);
        return sgfParseProperty(in);
    }
}

bool IO_sgf::do_HA(QTextStream &in)
{
    QString value;
    char tempChar;

    while(1)
    {
        in >> tempChar;
        if(tempChar != ']') value+=tempChar;
        else break;
    }

    int handicaps = value.toInt();
    if(handicaps>1 && handicaps<10)
    {
        tempSettings->write_HA(handicaps);
        return true;
    }
    else return false;
}

bool IO_sgf::do_KM(QTextStream &in)
{
    QString value;
    char tempChar;

    while(1)
    {
        in >> tempChar;
        if(tempChar != ']') value+=tempChar;
        else break;
    }

    double komi = value.toDouble();
    if(komi>=0)
    {
        tempSettings->write_KM(komi);
        return true;
    }
    else return false;
}

bool IO_sgf::do_RU(QTextStream &in)
{
    QString value;
    char tempChar;

    while(1)
    {
        in >> tempChar;
        if(tempChar != ']') value+=tempChar;
        else break;
    }

    string rule = value.toStdString();
    tempSettings->write_RU(rule);
    return true;
}

bool IO_sgf::do_GM(QTextStream &in)
{
    QString value;
    char tempChar;

    while(1)
    {
        in >> tempChar;
        if(tempChar != ']') value+=tempChar;
        else break;
    }

    int gameType = value.toInt();

    if(gameType == 1)
    {
        return true;
    }
    else return false;
}

bool IO_sgf::do_FF(QTextStream &in)
{
    QString value;
    char tempChar;

    while(1)
    {
        in >> tempChar;
        if(tempChar != ']') value+=tempChar;
        else break;
    }

    int fileFormat = value.toInt();
    assert(fileFormat>0 && fileFormat<5);
    tempSettings->write_FF(fileFormat);
    return true;
}

bool IO_sgf::do_AB(QTextStream &in)
{
    char x,y;
    char tempChar;

    in >> x >> y >> tempChar;

    if(tempChar == ']')
    {
        int ix = from_coo_sgf(x);
        int iy = from_coo_sgf(y);

        if(ix!=0 && iy!=0)
        {
            tempSettings->write_AB(ix,iy);

            qint64 tempPos = in.pos();
            in >> tempChar;
            if(tempChar == '[') return do_AB(in);
            else
            {
                in.seek(tempPos);
                return true;
            }
        }
        else return false;
    }
    else return false;
}

bool IO_sgf::do_SZ(QTextStream &in)
{
    QString value;
    char tempChar;

    while(1)
    {
        in >> tempChar;
        if(tempChar != ']') value+=tempChar;
        else break;
    }

    int boardSize = value.toInt();
    if(boardSize == 19)
    {
        return true;
    }
    else return false;
}

bool IO_sgf::do_BW(QTextStream &in, int color)
{
    char x,y;
    char tempChar;

    in >> x;
    if(x == ']')
    {
        coordinate coo;
        coo.first = 0;
        coo.second = 0;

        io_coordinate io_coo;
        io_coo.first = color;
        io_coo.second = coo;

        tempSeries->push_back(io_coo);

        return true;
    }
    else
    {
        in >> y >> tempChar;
        if(tempChar == ']')
        {
            coordinate coo;
            coo.first = from_coo_sgf(x);
            coo.second = from_coo_sgf(y);

            if(coo.first!=0 && coo.second!=0)
            {
                io_coordinate io_coo;
                io_coo.first = color;
                io_coo.second = coo;

                tempSeries->push_back(io_coo);

                return true;
            }
            else return false;
        }
        else return false;
    }
}

bool IO_sgf::do_Default(QTextStream &in)
{
    int root = 0; //保证']'与之前'['是对应的一对
    char tempChar;

    while(1)
    {
        in >> tempChar;
        if(tempChar == '[') root++; //在Comment属性C中有出现
        else if(tempChar == ']')
        {
            if(root==0) break;
            else root--;
        }
    }

    return true;
}

/***********************************/

char IO_sgf::o_start_tree()
{
    return '(';
}

char IO_sgf::o_end_tree()
{
    return ')';
}

char IO_sgf::o_start_node()
{
    return ';';
}

QString IO_sgf::o_set_stone(int color, int x, int y)
{
    QString str;
    QString cc;
    char cx,cy;

    if(color == BLACK) cc = "AB";
    else cc = "AW";

    cx = to_coo_sgf(x);
    cy = to_coo_sgf(y);

    str = cc + '[' + cx + cy + ']';

    return str;
}

QString IO_sgf::o_set_one_move(int color, int x, int y)
{
    QString str;
    QString cc;
    char cx,cy;

    if(color == BLACK) cc = "B";
    else cc = "W";

    if(x!=0 && y!=0) //not pass move
    {
        cx = to_coo_sgf(x);
        cy = to_coo_sgf(y);

        str = cc + '[' + cx + cy + ']' + '\n';
    }
    else str = cc + "[]" + '\n'; //pass move

    return str;
}

char IO_sgf::to_coo_sgf(int i)
{
    switch (i)
    {
        case 1: return 'a'; break;
        case 2: return 'b'; break;
        case 3: return 'c'; break;
        case 4: return 'd'; break;
        case 5: return 'e'; break;
        case 6: return 'f'; break;
        case 7: return 'g'; break;
        case 8: return 'h'; break;
        case 9: return 'i'; break;
        case 10: return 'j'; break;
        case 11: return 'k'; break;
        case 12: return 'l'; break;
        case 13: return 'm'; break;
        case 14: return 'n'; break;
        case 15: return 'o'; break;
        case 16: return 'p'; break;
        case 17: return 'q'; break;
        case 18: return 'r'; break;
        case 19: return 's'; break;
    }
    return '\0'; //error
}

int IO_sgf::from_coo_sgf(char c)
{
    switch (c)
    {
        case 'a': return 1; break;
        case 'b': return 2; break;
        case 'c': return 3; break;
        case 'd': return 4; break;
        case 'e': return 5; break;
        case 'f': return 6; break;
        case 'g': return 7; break;
        case 'h': return 8; break;
        case 'i': return 9; break;
        case 'j': return 10; break;
        case 'k': return 11; break;
        case 'l': return 12; break;
        case 'm': return 13; break;
        case 'n': return 14; break;
        case 'o': return 15; break;
        case 'p': return 16; break;
        case 'q': return 17; break;
        case 'r': return 18; break;
        case 's': return 19; break;
    }
    return 0; //error
}
